package com.gjw.demo.ctrl;

import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.gjw.demo.domain.Point;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;


@RestController
public class ImportController {

    @RequestMapping("/import")
    public String importExcel(MultipartFile file, HttpServletResponse response, HttpServletRequest request) {

        try (InputStream inputStream = file.getInputStream();
             ExcelReader excelReader = ExcelUtil.getReader(inputStream);
             ExcelWriter writer = excelReader.getWriter();
             ServletOutputStream out = response.getOutputStream();
        ) {
            for (int i = 0; i < excelReader.getSheetCount(); i++) {
                ExcelReader reader = excelReader.setSheet(i);
                writer.setSheet(i);
                List<List<Object>> read = reader.read(6, excelReader.getRowCount());
                int size = read.size();
                List<Point> li = new ArrayList<>();
                for (int j = 1; j < size - 1; j++) {
                    try {
                        addPoint(read.get(j - 1), li);
                        addPoint(read.get(j), li);
                        addPoint(read.get(j + 1), li);
                        Double k = FitCenterByLeastSquares(li);
                        writer.writeCellValue(8, j + 6, k.toString());
                        writer.setColumnWidth(8, 30);
                        writer.writeCellValue(9, j + 6, k);
                        System.out.println(k);
                        li.clear();
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
                System.out.println("---------------------------" + i + " : " + reader.getSheet().getSheetName() + "---------------------------");
            }

            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(("解析后_" + file.getOriginalFilename()), "UTF-8"));
            writer.flush(out, true);
        } catch (Exception e) {
            e.printStackTrace();
            return "解析失败!";
        }
        return null;
    }

    private void addPoint(List<Object> objects, List<Point> li) {
        Point point = new Point();
        point.setHeight(new BigDecimal((Double) objects.get(2)).setScale(4, RoundingMode.HALF_UP));
        point.setRingNum(new BigDecimal((Long) objects.get(7)).multiply(new BigDecimal("1.2")));
        li.add(point);
    }

    static double FitCenterByLeastSquares(List<Point> mapPoint) {
        BigDecimal sumX = BigDecimal.ZERO, sumY = BigDecimal.ZERO;
        BigDecimal sumXX = BigDecimal.ZERO, sumYY = BigDecimal.ZERO, sumXY = BigDecimal.ZERO;
        BigDecimal sumXXX = BigDecimal.ZERO, sumXXY = BigDecimal.ZERO, sumXYY = BigDecimal.ZERO, sumYYY = BigDecimal.ZERO;

        int pCount = mapPoint.size();
        for (int i = 0; i < pCount; i++) {
            sumX = sumX.add(mapPoint.get(i).getRingNum());
            sumY = sumY.add(mapPoint.get(i).getHeight());
            sumXX = sumXX.add(mapPoint.get(i).getRingNum().pow(2));
            sumYY = sumYY.add(mapPoint.get(i).getHeight().pow(2));
            sumXY = sumXY.add(mapPoint.get(i).getRingNum().multiply(mapPoint.get(i).getHeight()));
            sumXXX = sumXXX.add(mapPoint.get(i).getRingNum().pow(3));
            sumXXY = sumXXY.add(mapPoint.get(i).getRingNum().pow(2).multiply(mapPoint.get(i).getHeight()));
            sumXYY = sumXYY.add(mapPoint.get(i).getHeight().pow(2).multiply(mapPoint.get(i).getRingNum()));
            sumYYY = sumYYY.add(mapPoint.get(i).getHeight().pow(3));
        }

        BigDecimal pCountBigDecimal = new BigDecimal(pCount);
        BigDecimal M1 = pCountBigDecimal.multiply(sumXY).subtract(sumX.multiply(sumY));
        BigDecimal M2 = pCountBigDecimal.multiply(sumXX).subtract(sumX.multiply(sumX));
        BigDecimal M3 = pCountBigDecimal.multiply(sumXXX.add(sumXYY)).subtract(sumX.multiply(sumXX.add(sumYY)));
        BigDecimal M4 = pCountBigDecimal.multiply(sumYY).subtract(sumY.multiply(sumY));
        BigDecimal M5 = pCountBigDecimal.multiply(sumYYY.add(sumXXY)).subtract(sumY.multiply(sumXX.add(sumYY)));

        BigDecimal a = M1.multiply(M5).subtract(M3.multiply(M4)).divide(M2.multiply(M4).subtract(M1.multiply(M1)), 100, BigDecimal.ROUND_HALF_UP);
        BigDecimal b = M1.multiply(M3).subtract(M2.multiply(M5)).divide(M2.multiply(M4).subtract(M1.multiply(M1)), 100, BigDecimal.ROUND_HALF_UP);
        BigDecimal c = (a.multiply(sumX).add(b.multiply(sumY)).add(sumXX).add(sumYY)).divide(pCountBigDecimal, 100, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("-1"));

        //圆心XY 半径
        BigDecimal xCenter = new BigDecimal(-0.5).multiply(a);
        BigDecimal yCenter = new BigDecimal(-0.5).multiply(b);
        BigDecimal radius = new BigDecimal(0.5).multiply(sqrt(a.multiply(a).add(b.multiply(b).subtract(new BigDecimal("4").multiply(c))), 100));
        a = sumXY.subtract(sumX.multiply(sumY).divide(pCountBigDecimal, 100, BigDecimal.ROUND_HALF_UP)).divide(sumXX.subtract(sumX.multiply(sumX).divide(pCountBigDecimal, 100, BigDecimal.ROUND_HALF_UP)), 100, BigDecimal.ROUND_HALF_UP);
        b = sumY.subtract(a.multiply(sumX)).divide(pCountBigDecimal, 100, BigDecimal.ROUND_HALF_UP);
        double k = (xCenter.multiply(a).add(b).compareTo(yCenter) <= 0 ? (new BigDecimal("1").divide(radius, 100, BigDecimal.ROUND_HALF_UP)) : (new BigDecimal("-1").divide(radius, 100, BigDecimal.ROUND_HALF_UP))).doubleValue();
        return k;
    }

    public static BigDecimal sqrt(BigDecimal value, int scale) {
        BigDecimal num2 = BigDecimal.valueOf(2);
        int precision = 100;
        MathContext mc = new MathContext(precision, RoundingMode.HALF_UP);
        BigDecimal deviation = value;
        int cnt = 0;
        while (cnt < precision) {
            deviation = (deviation.add(value.divide(deviation, mc))).divide(num2, mc);
            cnt++;
        }
        deviation = deviation.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return deviation;
    }
}
